/*
	flad : Copyright 2010 Dale J Williams

	Licensed under the Apache License, Version 2.0 (the "License");
	you may not use this file except in compliance with the License.
	You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
*/
package lads.flad.sound {
	
	/**
	 * SoundManager: Contains and controls game sounds, tracks.
	 * @author Dale J Williams
	 * @version 1.1
	 */
	public class SoundManager {
		
		private var _sounds		:Vector.<SoundObject>;
		private var _tracks		:Vector.<SoundTrack>;	
		private var _groups		:Vector.<SoundGroup>;	
		private var _default	:SoundTrack;
		
		private var _volume		:Number;
		
		public function SoundManager() {
			init();
		}
		
		private function init():void {
			_sounds = new Vector.<SoundObject>();
			_tracks = new Vector.<SoundTrack>();
			_groups = new Vector.<SoundGroup>();
			
			_default = new SoundTrack("default");
			addTrack(_default);
			
			_volume = 1;
		}
		
		public function playSound($soundOrSoundName:Object, $overrideTrackOrTrackName:Object = null, $overrideVolume:Number = -1):void {
			var sound:SoundObject = getSoundFromObject($soundOrSoundName);
			if (!sound) throw new Error("Object " + $soundOrSoundName + " is not a SoundObject or name.");
			var track:SoundTrack = $overrideTrackOrTrackName ? getTrackFromObject($overrideTrackOrTrackName) : sound.track;
			if (!track) 
				track = _default;
			if ($overrideVolume > 0) 
				track.volume = $overrideVolume;
			trace("playSound " + sound.name), 
			track.playSound(sound);
		}
		
		public function stopSound($soundOrSoundName:Object, $hardStop:Boolean=false):void {
			var sound:SoundObject = getSoundFromObject($soundOrSoundName);
			if (!sound) throw new Error("Object for stopping is not a SoundObject or name.");
			if (!sound.track) return;
			sound.track.stopSound(sound, $hardStop);
		}
		
		public function stopAllSounds($hardStop:Boolean = false):void {
			for each(var sound:SoundObject in _sounds)
				stopSound(sound);
		}
		
		public function addSound($sound:SoundObject, $trackOrTrackName:Object = null):SoundObject {
			var track:SoundTrack = getTrackFromObject($trackOrTrackName);
			if (!track) track = _default;
			$sound.track = track;
			_sounds.push($sound);
			return $sound;
		}
		
		public function removeSound($soundOrSoundName:Object):SoundObject {
			var sound:SoundObject = getSoundFromObject($soundOrSoundName);
			if (!sound) throw new Error("Object for removal is not a SoundObject or name.");
			var i:int = _sounds.length;
			while (i--) {
				if (_sounds[i] == sound)
					return _sounds.splice(i, 1)[0];
			}
			throw new Error("SoundManager does not contain specified sound.");
			return null;
		}
				
		public function getSound($soundName:String):SoundObject {
			var i:int = _sounds.length;
			while (i--) {
				if (_sounds[i].name == $soundName)
					return _sounds[i];
			}
			return null;
		}
		
		public function addTrack($trackOrTrackName:Object, $numChannels:int=-1, $fadeTime:Number=0, $volume:Number=1):SoundTrack {
			var track:SoundTrack;
			var trackName:String;
			if ($trackOrTrackName is SoundTrack) {
				track = $trackOrTrackName as SoundTrack;
				trackName = track.name;
			} else if ($trackOrTrackName is String) {
				trackName = $trackOrTrackName as String;
				track = new SoundTrack(trackName, $numChannels, $fadeTime, $volume);
			} else {
				throw new Error("Object " + $trackOrTrackName + " is not a SoundTrack or name.");			
			}
			if (!getTrack(trackName))
				_tracks.push(track);		
			else return null;
			track.manager = this;
			return track;
		}
		
		public function removeTrack($trackOrTrackName:Object):SoundTrack {
			var track:SoundTrack = getTrackFromObject($trackOrTrackName);
			if (!track) throw new Error("Object for removal is not a SoundTrack or name.");
			var i:int = _tracks.length;
			while (i--) {
				if (_tracks[i] == track) {
					_tracks.splice(i, 1)[0];
					track.stopAllSounds();
					track.manager = null;
					break;
				}
			}
			return track;
		}
		
		public function getTrack($trackName:String):SoundTrack {
			var i:int = _tracks.length;
			while (i--)
				if (_tracks[i].name == $trackName)
					return _tracks[i];
			return null;
		}
		
		public function addGroup($groupName:String):SoundGroup {
			var result:SoundGroup = getGroup($groupName);
			if (result) return result;
			result = new SoundGroup($groupName);
			_groups.push(result);
			return result;
		}
		
		public function addSoundToGroup($soundObjectOrName:Object, $groupName:String):void {
			var group:SoundGroup = getGroup($groupName);
			group.addSound(getSoundFromObject($soundObjectOrName));
		}
		
		public function playGroup($groupName:String):void {
			playSound(getGroup($groupName).getRandom());
		}
		
		public function getGroup($groupName:String):SoundGroup {
			var i:int = _groups.length;
			while (i--) {
				if (_groups[i].name == $groupName)
					return _groups[i];
			}
			return null;
		}
		
		private function getSoundFromObject($soundOrSoundName:Object):SoundObject {
			if ($soundOrSoundName is String)
				return getSound(String($soundOrSoundName));
			else if ($soundOrSoundName is SoundObject)
				return SoundObject($soundOrSoundName);
			return null;
		}
		
		private function getTrackFromObject($trackOrTrackName:Object):SoundTrack {
			if ($trackOrTrackName is String)
				return getTrack(String($trackOrTrackName));
			else if ($trackOrTrackName is SoundTrack)
				return SoundTrack($trackOrTrackName);
			return null;
		}
		
		private function updateVolume():void {
			for each(var track:SoundTrack in _tracks)
				track.updateVolume();
		}
		
		public function get volume():Number { return _volume; }		
		public function set volume(value:Number):void {
			_volume = value;
			updateVolume();
		}
		
	}

}